#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <signal.h>


#include "estructuras.h"
#include "constantes.h"
#include "listas.h"
#include "sockets.h"
#include "configuracion.h"
#include "NIPC.h"
#include "RAID.h"

extern ListConfig * listaConfig; 
void * AtenderPFS(void * args);

int numeroSincro;
int contador;
pthread_mutex_t mutexnumped = PTHREAD_MUTEX_INITIALIZER;

int main ()
{	
   
    struct sockaddr_in sin;
    int iAddress_size= sizeof(struct sockaddr_in);
    fd_set set_socketsHiloPrincipal;			/**lista de sockets que se van a hacer que se active el select del hilo principal cuando reciban algo**/		
    int iSocketEscuchaPedidosFSS,iSocket_temporal,iMayorSocket;	
	int *pSocketAux,*pSocketArrayParaConsola;	
    pthread_t ID_HiloParaConsolaEsperarConexionDiscos;
    int ret;
	cliente *pNodoCliente,*pAuxCliente;
	char * sContador=(char*)malloc(20);
	char *sSocket=(char *)malloc(20);
	numeroSincro=0;
	
    /*****INICIALIZACION DE VARIABLES******/
    Estado= FUERA_DE_SERVICIO;
    SubEstado = INCOMPLETO;
    ListaDiscos = NULL;
    ListaDiscos_Ult = NULL;
	ListaClientes = NULL;
    ListaClientes_Ult = NULL;	
	colaGlobal=NULL;
	colaGlobal_Ult=NULL;
	colaEspera=NULL;
	colaEspera_Ult=NULL;
    /************************************/
	
	listaConfig = NULL;
	cargarConfig();
	system("clear");
	printf("\nCargando Archivo de Configuracion...\n\n");
    puertoFSS=atoi(obtenerValor("puertoFSS"));
	puertoConex=atoi(obtenerValor("puertoConex"));
	printf("puertoFSS %i \n",puertoFSS);
    printf("puertoConex %i \n",puertoConex);
	pSocketArrayParaConsola =malloc(sizeof(int));
	pSocketAux = malloc(sizeof(int));						
	CrearYConectarDosSockets(pSocketArrayParaConsola,pSocketAux,obtenerValor("ipLocal"));
	ret=pthread_create(&ID_HiloParaConsolaEsperarConexionDiscos,NULL,(void *)ConsolaEsperaNuevosDiscos ,(void *)pSocketAux);    
	
    InfoIPC rInfoIPC;
	recibirIPC(*pSocketArrayParaConsola, &rInfoIPC); /*RAID pasa a estar operacional*/
	free(rInfoIPC.payload);
	
	pthread_mutex_init(&mutex,NULL); /*mutex para la cola global*/
	pthread_mutex_init(&mutex2,NULL); /*mutex para la cola de espera de escritura*/
	pthread_mutex_init(&mutex3,NULL);/*mutex para el contador que indica numero de pedido*/
	pthread_mutex_init(&mutex4,NULL);/*mutex para la lista de discos*/

	
	iSocketEscuchaPedidosFSS=PrepararSocket (puertoFSS,obtenerValor("ipLocal"));   
	escuchar(iSocketEscuchaPedidosFSS,1000000);
    
	contador=0;	

	listaPFS *nuevoPFS;
	
	while(1)
	{			
		/*FD_ZERO(&set_socketsHiloPrincipal);
				FD_SET(iSocketEscuchaPedidosFSS, &set_socketsHiloPrincipal);
				iMayorSocket = iSocketEscuchaPedidosFSS;
			
				pAuxCliente = ListaClientes;
				while(pAuxCliente != NULL)
				{
					FD_SET(pAuxCliente->socketCliente , &set_socketsHiloPrincipal);
					if(pAuxCliente->socketCliente > iMayorSocket)
						iMayorSocket = pAuxCliente->socketCliente;
					pAuxCliente = pAuxCliente->Sgt;
				}
			
				
				select(iMayorSocket + 1,&set_socketsHiloPrincipal, NULL, NULL, NULL);*/

				
				/*ACEPTO CONEXIONES DE CLIENTES FSS*/
				/*if( FD_ISSET(iSocketEscuchaPedidosFSS, &set_socketsHiloPrincipal ) > 0 )
				{*/

					nuevoPFS = (listaPFS *) malloc (sizeof(listaPFS));
					nuevoPFS->iSocket = 0;

					if((iSocket_temporal = accept (iSocketEscuchaPedidosFSS,(struct sockaddr *)&sin,&iAddress_size ))== -1)
						return ERRORACCEPT;

					nuevoPFS->iSocket = iSocket_temporal;
					/*printf("Se conecto el cliente  %i \n", nuevoPFS->iSocket);*/
					nuevoPFS->flag_cerrar = 0;

					recibirIPC(nuevoPFS->iSocket,&rInfoIPC);
					free(rInfoIPC.payload);


					/*InfoIPC rInfoIPC2;
					recibirIPC(iSocket_temporal,&rInfoIPC2);
					free(rInfoIPC2.payload);*/
				
				
					if (iCantidadDeDAConectados == 0) {
		                printf("\n\nConexion rechazada, no hay discos conectados \n\n");
						InfoIPC rInfoIPC3;
						rInfoIPC.payLoadLength=5;
						generarStructIPC("nada",TERMINARCONEXION,&rInfoIPC3);
						enviarIPC(iSocket_temporal,&rInfoIPC3);
						free(rInfoIPC3.payload);
						InfoIPC rInfoIPC4;
						recibirIPC(iSocket_temporal,&rInfoIPC4);
						cerrarSocket(iSocket_temporal);
						free(rInfoIPC4.payload);
						continue;
					}


					/*pNodoCliente = (cliente *)malloc(sizeof(cliente));
					pNodoCliente->socketCliente=iSocket_temporal;

					agregarAListaDeClientes(&pNodoCliente);
					imprimirCola(ListaClientes);*/


					InfoIPC hsRInfoIPC;
					hsRInfoIPC.payLoadLength = 5;
					generarStructIPC("nada",HSOK,&hsRInfoIPC);
					enviarIPC(nuevoPFS->iSocket,&hsRInfoIPC);
					free(hsRInfoIPC.payload);

					pthread_t ID_HiloAtenderPFS;
					ret=pthread_create(&ID_HiloAtenderPFS,NULL,(void *)AtenderPFS,(void *)&(*nuevoPFS));
					if(ret!=0){
						perror("pthread_create");
						exit(EXIT_FAILURE);
					}

					/*InfoIPC rInfoIPC5;
					rInfoIPC5.payLoadLength=5;
					generarStructIPC("nada",HSOK,&rInfoIPC5);
					enviarIPC(pNodoCliente->socketCliente,&rInfoIPC5);
					free(rInfoIPC5.payload);*/

				/*}*/
				
				
				/*RECIBO PEDIDOS DE ALGUN SOCKET CLIENTE FSS*/
				/*pAuxCliente = ListaClientes;
				while(pAuxCliente != NULL)
				{
					if( FD_ISSET(pAuxCliente->socketCliente , &set_socketsHiloPrincipal ) > 0 ){
						break;
					}
					pAuxCliente = pAuxCliente->Sgt;
				}
				if(pAuxCliente != NULL)
				{	*/
						

				/*}*/
	}
	free(pSocketArrayParaConsola);
	free(pSocketAux);
	return 0;
}


int asignarPedido(struct ListaIDdescriptors ** nodoPedido) {
   
	disco *pAuxDisco;
	disco *dsk;
	int payload;
	char *sAux=(char *)malloc(20);
	int tamanio_buff;
	
	if ((*nodoPedido)->operacion==0) {
			
		dsk = (disco *)elegirDisco((*nodoPedido)->numeroSector);
		
		
		if(dsk==NULL){
			printf("No hay disco listo para atender esa solicitud \n");
			return -1;
		}	
			
		
		strcpy((*nodoPedido)->discoAsignado,dsk->sNombre);
		dsk->pendings++;
		memset(sAux,'\0',20);
		intacad((* nodoPedido)->numeroSector,sAux);	
		payload=READ_SECTOR_REQUEST;
		tamanio_buff = strlen((* nodoPedido)->sDescriptorID_delPedido) + 1 + strlen(sAux) + 2;
		char* buffer = (char *) malloc (tamanio_buff);
		strcpy(buffer, (* nodoPedido)->sDescriptorID_delPedido);
		strcat(buffer, ",");
		strcat(buffer, sAux);
		strcat(buffer, ";");
		InfoIPC rInfoIPC3;
		rInfoIPC3.payLoadLength=tamanio_buff;
		generarStructIPC(buffer,payload,&rInfoIPC3);
		enviarIPC(dsk->iSocketHDAParaDA,&rInfoIPC3);	
		free(rInfoIPC3.payload);
		(* nodoPedido)->estado = 1;
		/*printf("Disco elegido para realizar lectura del sector %i es: %s \n",(*nodoPedido)->numeroSector,dsk->sNombre);*/
		CambiaPosicionDelNodo(&dsk);
		free(buffer);
		
	} else {
		
		strcpy((*nodoPedido)->discoAsignado,"");
		memset(sAux,'\0',20);
		intacad((* nodoPedido)->numeroSector,sAux);	
		payload=WRITE_SECTOR_REQUEST;
		pAuxDisco = ListaDiscos;	
		while(pAuxDisco != NULL){	
		
			if((pAuxDisco->Estado!=SINCRONIZANDO)){
			
				pAuxDisco->pendings++;
				tamanio_buff = strlen((* nodoPedido)->sDescriptorID_delPedido) + 1 + strlen(sAux) + 1 + 512 + 1;
				char *buffer = (char *) malloc(tamanio_buff);
				strcpy(buffer, (* nodoPedido)->sDescriptorID_delPedido);
				strcat(buffer, ",");
				strcat(buffer, sAux);
				strcat(buffer, ";");
				memcpy(buffer + strlen((* nodoPedido)->sDescriptorID_delPedido) + 1 + strlen(sAux) + 1, (* nodoPedido)->data, 512);
				InfoIPC rInfoIPC35;
				rInfoIPC35.payLoadLength=tamanio_buff;
				generarStructIPC(buffer,payload,&rInfoIPC35);
				enviarIPC(pAuxDisco->iSocketHDAParaDA,&rInfoIPC35);
				free(rInfoIPC35.payload);
				(* nodoPedido)->estado = 1;
				/*printf("Se asigno escritura del sector %i al disco: %s \n",(*nodoPedido)->numeroSector,pAuxDisco->sNombre);*/
				free(buffer);
			}
			
			pAuxDisco=pAuxDisco->Sgt;
		}	
	}
	free(sAux);
	return 0;
}
	



disco * elegirDisco(int numeroSector) {
	
	int count = 32767;
	disco *pAuxDisco;
	disco *pDiscoElegido;
	
	pAuxDisco=NULL;
	pDiscoElegido=NULL;
	
	pAuxDisco = ListaDiscos;
	
	
	while(pAuxDisco != NULL){
		
		if (((pAuxDisco->Estado==LISTO)||(pAuxDisco->Estado==SINCRONIZANDO_LEYENDO)) && (pAuxDisco->pendings < count)) {	
			pDiscoElegido=pAuxDisco;
			count = pAuxDisco->pendings;
		} 
		
		pAuxDisco=pAuxDisco->Sgt;
		
	}
	
    return pDiscoElegido;
}	




void * AtenderPFS(void * args)
{
	listaPFS *pAuxCliente = (listaPFS *) args;
	char * sIDdescriptor_delPedido=(char *)malloc(20);
	char * sSocket=(char *)malloc(20);
	char * sContador=(char*)malloc(20);

	InfoIPC rInfoIPC;
	InfoIPC rInfoIPC2;
	int rc;

	while(1)
	{
			InfoIPC rInfoIPC6;
			if(recibirIPC(pAuxCliente->iSocket,&rInfoIPC6)<0){
				pAuxCliente->flag_cerrar = 1;
				break;
			}

			pedido *NodoDescriptores = (pedido *)malloc(sizeof(pedido));

			/*Lleno el nodo de pedido*/
			intacad(pAuxCliente->iSocket,sSocket);
			strcpy(NodoDescriptores->sSocketPorElCualResponder,sSocket);
			NodoDescriptores->numeroSector=atoi(rInfoIPC6.payload);
			NodoDescriptores->Sgt = NULL;
			NodoDescriptores->Ant = NULL;

			if(pthread_mutex_lock(&mutexnumped)!=0)
			{
						perror("pthread_mutex_lock");
						exit(EXIT_FAILURE);
			}
			contador++;
			intacad(contador,sContador);
			if(pthread_mutex_unlock(&mutexnumped)!=0)
			{
						perror("pthread_mutex_unlock");
						exit(EXIT_FAILURE);
			}

			strcpy(NodoDescriptores->sDescriptorID_delPedido,sContador);


			if((rInfoIPC6.payLoadDescriptor == READ_SECTOR_REQUEST) || (rInfoIPC6.payLoadDescriptor == READ_SECTOR_REQUEST_LAST))
			{
				NodoDescriptores->operacion=0;
				/*printf("Recibi pedido de lectura del sector %i \n", NodoDescriptores->numeroSector);*/

				/*Lo agrego a la cola global*/
				if(pthread_mutex_lock(&mutex)!=0){
					perror("pthread_mutex_lock");
					exit(EXIT_FAILURE);
				}

				AgregarAColaGlobal(&NodoDescriptores,&colaGlobal,&colaGlobal_Ult);

				if(pthread_mutex_unlock(&mutex)!=0){
					perror("pthread_mutex_unlock");
					exit(EXIT_FAILURE);
				}

				NodoDescriptores->discosAEspejar=0;
				free(rInfoIPC6.payload);
			}

			if(rInfoIPC6.payLoadDescriptor == WRITE_SECTOR_REQUEST)
			{

				NodoDescriptores->operacion=1;

				if(pthread_mutex_lock(&mutex4)!=0){
					perror("pthread_mutex_lock");
					exit(EXIT_FAILURE);
				}

				NodoDescriptores->discosAEspejar=longitud2(ListaDiscos);

				if(pthread_mutex_unlock(&mutex4)!=0){
					perror("pthread_mutex_lock");
					exit(EXIT_FAILURE);
				}

				printf("longitud2(ListaDiscos) %i \n",NodoDescriptores->discosAEspejar);
				InfoIPC rInfoIPC30;
				recibirIPC(pAuxCliente->iSocket,&rInfoIPC30);
				memcpy(NodoDescriptores->data,rInfoIPC30.payload, 512);
				free(rInfoIPC30.payload);
				/*printf("Recibi pedido de escritura del sector %i con la sgte data: %s\n", NodoDescriptores->numeroSector,NodoDescriptores->data);*/


				if(pthread_mutex_lock(&mutex)!=0){
					perror("pthread_mutex_lock");
					exit(EXIT_FAILURE);
				}

				AgregarAColaGlobal(&NodoDescriptores,&colaGlobal,&colaGlobal_Ult);

				if(pthread_mutex_unlock(&mutex)!=0){
					perror("pthread_mutex_unlock");
					exit(EXIT_FAILURE);
				}

				free(rInfoIPC6.payload);
			}

			asignarPedido(&NodoDescriptores);

		}

		if(pAuxCliente->flag_cerrar == 1)
		{
			/*printf("Se cierra el cliente %i\n", pfs->iSocket);*/
			close(pAuxCliente->iSocket);
		}

		free(sIDdescriptor_delPedido);
		free(sSocket);

		pthread_detach(pthread_self());
	}

